//
// Created by lz on 2021/2/23.
//

#include "Thread.h"
#include "base/CurrentThread.h"
#include "base/Exception.h"
#include "base/Logging.h"

#include <type_traits>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <linux/unistd.h>

namespace lzweb
{
	namespace detail
	{
		//通过系统调用获取线程id
		pid_t  gettid()
		{
			return static_cast<pid_t>(syscall(SYS_gettid));
		}

		void afterFork()
		{
			CurrentThread::t_cachedTid = 0;
			CurrentThread::t_threadName = "main";
			CurrentThread::tid();
			// no need to call pthread_atfork(NULL, NULL, &afterFork);
		}
		class ThreadNameInitializer
		{
		 public:
			ThreadNameInitializer()
			{
				lzweb::CurrentThread::t_threadName = "main";
				CurrentThread::tid();
				pthread_atfork(NULL,NULL,&afterFork);
			}
		};

		ThreadNameInitializer init;

		struct ThreadData
		{
			using ThreadFunc = Thread::ThreadFunc;
			ThreadFunc func_;
			string name_;
			pid_t * tid_;
			CountDownLatch* latch_;

			ThreadData(ThreadFunc func,
				const string& name,
				pid_t* tid,
				CountDownLatch* latch)
				:func_(func),
				name_(name),
				tid_(tid),
				latch_(latch)
			{}

			void runInThread()
			{
				*tid_ = CurrentThread::tid();
				tid_ = NULL;
				latch_->countDown();
				latch_ = NULL;

				CurrentThread::t_threadName = name_.empty()? "lzwebThread": name_.c_str();

				//int prctl ( int option,unsigned long arg2,unsigned long arg3,unsigned long arg4,unsigned long arg5 )
				//这个系统调用指令是为进程制定而设计的，明确的选择取决于 option:
				//PR_SET_NAME :把参数arg2作为调用进程的经常名字。（SinceLinux 2.6.11）
				prctl(PR_SET_NAME,CurrentThread::t_threadName);
				try {
					func_();
					CurrentThread::t_threadName = "finished";

				}catch (const Exception& ex)
				{
					CurrentThread::t_threadName = "crashed";
					fprintf(stderr,"exception caught in Thread %s\n", name_.c_str());
					fprintf(stderr,"reason: %s\n",ex.what());
					fprintf(stderr,"stack trace: %s\n", ex.stackTrace());
					abort();
				}

				catch (const std::exception& ex)
				{
					CurrentThread::t_threadName = "crashed";
					fprintf(stderr, "exception caught in Thread %s\n", name_.c_str());
					fprintf(stderr, "reason: %s\n", ex.what());
					abort();
				}
				catch (...)
				{
					CurrentThread::t_threadName = "crashed";
					fprintf(stderr, "unknown exception caught in Thread %s\n", name_.c_str());
					throw; // rethrow
				}

			}

		};

		void* startThread(void* obj)
		{
			ThreadData* data = static_cast<ThreadData*>(obj);
			data->runInThread();
			delete data;
			return NULL;
		}


	}// namespace detail

	void CurrentThread::cacheTid()
	{
		if(t_cachedTid == 0)
		{
			t_cachedTid =detail::gettid();
			t_tidStringLength = snprintf(t_tidString, sizeof t_tidString, "%5d ", t_cachedTid);
		}
	}

	bool CurrentThread::isMainThread()
	{
		//tid()返回有内核管理的唯一标志线程号pid，主线程的pid和其进程pid标志相同
		return tid() == getpid();
	}

	//睡眠usec毫秒
	void CurrentThread::sleepUsec(int64_t usec)
	{
		struct timespec ts ={0,0};
		ts.tv_sec = static_cast<time_t>(usec / TimeStamp::kMicroSecondsPerSecond);
		ts.tv_nsec = static_cast<long>(usec % TimeStamp::kMicroSecondsPerSecond * 1000);
		nanosleep(&ts,NULL);
	}

	//默认初始化
	AtomicInt32 Thread::numCreated_;

	//默认设置
	Thread::Thread(ThreadFunc func, const string& name)
	:started_(false),
	joined_(false),
	pthreadId_(0),
	tid_(0),
	func_(std::move(func)),
	name_(name),
	latch_(1)
	{
		setDefaultName();
	}

	Thread::~Thread()
	{
		if(started_ && !joined_)
		{
			pthread_detach(pthreadId_);
		}
	}

	//如果没有指定的name，设置默认的名字
	void Thread::setDefaultName()
	{
		int num = numCreated_.incrementAndGet();
		if(name_.empty())
		{
			char buf[32];
			snprintf(buf,sizeof buf,"Thread%d", num);
			name_ = buf;
		}
	}


	void Thread::start()
	{
		assert(!started_);
		started_ = true;
		// FIXME: move(func)

		detail::ThreadData* data = new detail::ThreadData(func_,name_,&tid_,&latch_);
		if(pthread_create(&pthreadId_,NULL,&detail::startThread,data))
		{
			started_ = false;
			delete data;
			LOG_SYSFATAL << "Failed in pthread_create";
		}else
		{
			//下面这行代码保证了新线程执行到countdown，assert()语句才会被执行
			latch_.wait();
			assert(tid_ > 0);
		}
	}

	int Thread::join()
	{
		assert(started_);
		assert(!joined_);
		joined_ = true;
		return pthread_join(pthreadId_, NULL);
	}

}//namespace lzweb
